package com.wh.wisdomsite.xbox.common.client;

import com.wh.wisdomsite.xbox.common.annotation.MessageFormat;
import com.wh.wisdomsite.xbox.common.config.SystemParameterNames;
import com.wh.wisdomsite.xbox.common.util.XboxSign;
import com.wh.wisdomsite.xbox.common.XboxRequest;
import com.wh.wisdomsite.xbox.common.annotation.DeviceType;
import com.wh.wisdomsite.xbox.utils.StringUtils;
import com.wh.wisdomsite.xbox.utils.json.JosnConverter;
import com.wh.wisdomsite.xbox.utils.json.impl.FastJsonConverter;
import com.wh.wisdomsite.xbox.utils.json.impl.XmlConverter;
import com.wh.wisdomsite.xbox.common.reponse.ErrorResponse;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;
import java.lang.reflect.Field;
import java.util.*;


/***
 * 默认客户端服务
 * @Package com.wh.wisdomsite.xbox.entity.client
 * @author 谢泽鹏
 * @date 2016年11月07日 下午15:10
 * @Copyright 个人版权所有
 * @Company 贵州万华科技有限公司Copyright (c) 2016
 * @version V1.0
 */
public class DefaultXboxClient implements XboxClient {

    private static final Log logger = LogFactory.getLog(DefaultXboxClient.class);

    /**
     * 服务地址
     */
    private String serverUrl;

    /**
     * 应用键
     */
    private String appKey;

    /**
     * 应用密钥
     */
    private String singKey;

    /**
     * 会话标识
     */
    private String sessionId;

    /**
     * 客户端请求时间戳
     */
    private Long requestTimestamp;

    /**
     * 应用平台
     */
    private String client;

    /**
     * 应用类型 PC, ANDROID, IOS, WAP
     */
    private String callType = DeviceType.PC.toString();


    /**
     * 应用版本
     */
    private String callVersion;


    /**
     * 报文格式
     */
    private MessageFormat messageFormat = MessageFormat.json;

    /**
     * 中文编码
     */
    private Locale locale = Locale.SIMPLIFIED_CHINESE;

    /**
     * JSON转换器
     */
    private JosnConverter xmlConverter = new XmlConverter();

    /**
     * JSON转换器
     */
    private JosnConverter fastJsonConverter = new FastJsonConverter();

    /**
     * 请求类所有请求参数
     */
    private Map<Class<?>, List<Field>> requestAllFields = new HashMap<Class<?>, List<Field>>();

    /**
     * 请求类所有不需要进行签名的参数
     */
    private Map<Class<?>, List<String>> requestIgnoreSignFieldNames = new HashMap<Class<?>, List<String>>();

    private RestTemplate restTemplate = new RestTemplate();

    /**
     * 构造函数1
     * @param serverUrl 请求地址
     * @param appKey    应用键
     * @param singKey 应用秘钥
     */
    public DefaultXboxClient(String serverUrl, String appKey, String singKey) {
        this.serverUrl = serverUrl;
        this.appKey = appKey;
        this.singKey = singKey;
    }


    /**
     * 构造函数2
     * @param serverUrl  请求地址
     * @param appKey     应用键
     * @param appSecret  应用秘钥
     * @param messageFormat 消息格式
     */
    public DefaultXboxClient(String serverUrl, String appKey, String appSecret, MessageFormat messageFormat) {
        this.serverUrl = serverUrl;
        this.appKey = appKey;
        this.singKey = appSecret;
        this.messageFormat = messageFormat;
    }


    /**
     * 构造函数3
     * @param serverUrl   请求地址
     * @param appKey      应用键
     * @param singKey     应用秘钥
     * @param messageFormat 消息格式
     * @param locale      语言
     */
    public DefaultXboxClient(String serverUrl, String appKey, String singKey, MessageFormat messageFormat, Locale locale) {
        this.serverUrl = serverUrl;
        this.appKey = appKey;
        this.singKey = singKey;
        this.messageFormat = messageFormat;
        this.locale = locale;
    }

    @Override
    public void setSessionId(String sessionId) {
        this.sessionId = sessionId;
    }


    @Override
    public ClientRequest buildClientRequest() {
        return new DefaultClientRequest("sync");
    }

    @Override
    public ClientRequest buildAsyncClientRequest() {
        return new DefaultClientRequest("async");
    }

    public MessageFormat getMessageFormat() {
        return messageFormat;
    }

    public void setMessageFormat(MessageFormat messageFormat) {
        this.messageFormat = messageFormat;
    }

    public Locale getLocale() {
        return locale;
    }

    public void setLocale(Locale locale) {
        this.locale = locale;
    }


    public String getCallType() {
        return callType;
    }

    public void setCallType(String callType) {
        this.callType = callType;
    }

    public void setRequestTimestamp(){
        this.requestTimestamp = System.currentTimeMillis();
    }

    public Long getRequestTimestamp(){
        setRequestTimestamp();
        return this.requestTimestamp;
    }


    public String getClient() {
        return client;
    }

    public void setClient(String client) {
        this.client = client;
    }

    public String getCallVersion() {
        return callVersion;
    }

    public void setCallVersion(String callVersion) {
        this.callVersion = callVersion;
    }


    /**
     *
     */
    private class DefaultClientRequest implements ClientRequest {
        //绑定对象
        private String executionType;
        //参数Map集合
        private Map<String, String> paramMap = new HashMap<String, String>(20);

        /**
         * 构造函数
         * @param async
         */
        private DefaultClientRequest(String async) {
            paramMap.put(SystemParameterNames.getAppKey(), appKey);
            paramMap.put(SystemParameterNames.getFormat(), messageFormat.name());
            paramMap.put(SystemParameterNames.getLocale(), locale.toString());
            if (null != sessionId) {
                paramMap.put(SystemParameterNames.getSessionId(), sessionId);
            }
            executionType = async;
        }

        /**
         * 清空参数Map
         * @return
         */
        @Override
        public ClientRequest clearParam() {
            paramMap.clear();
            return this;
        }

        /**
         * 添加参数信息
         * @param paramName  参数名
         * @param paramValue 参数值
         * @return
         */
        @Override
        public ClientRequest addParam(String paramName, Object paramValue) {
            logger.error("DefaultClientRequest->>addParam: paramName " +  "参数名不能为空");
            logger.error("DefaultClientRequest->>addParam: paramValue " +  "参数值不能为空");
            paramMap.put(paramName, paramName);
            return this;
        }


        /**
         * 发起POST请求
         * @param clazz      类对象
         * @param methodName 方法名
         * @param version    版本号
         * @param <T>        返回类型
         * @return
         */
        @Override
        public <T> CompositeResponse post(Class<T> clazz, String methodName, String version) {
            Map<String, String> requestParams = addOtherSystemParams(methodName, version);
            return post(clazz, requestParams);
        }


        /**
         * 发起POST请求(带会话标识)
         * @param clazz      类对象
         * @param methodName 方法名
         * @param version    版本号
         * @param sessionId  会话标识
         * @param <T>        返回类型
         * @return
         */
        @Override
        public <T> CompositeResponse post(Class<T> clazz, String methodName, String version, String sessionId) {
            Map<String, String> requestParams = addOtherSystemParams(methodName, version, sessionId);
            return post(clazz, requestParams);
        }


        /**
         * 发起POST请求(直接使用)
         * @param xboxRequest 接口对象
         * @param clazz      类对象
         * @param methodName 方法名
         * @param version    版本号
         * @param <T>        返回类型
         * @return
         */
        @Override
        public <T> CompositeResponse post(XboxRequest xboxRequest, Class<T> clazz, String methodName, String version) {
            Map<String, String> requestParams = getRequestForm(xboxRequest, methodName, version);
            return post(clazz, requestParams);
        }


        /**
         * 发起POST请求,(直接使用并带会话标识)
         * @param xboxRequest  接口对象
         * @param clazz      类对象
         * @param methodName 方法名
         * @param version    版本号
         * @param sessionId  会话标识
         * @param <T>        返回类型
         * @return
         */
        @Override
        public <T> CompositeResponse post(XboxRequest xboxRequest, Class<T> clazz, String methodName, String version, String sessionId) {
            Map<String, String> requestParams = getRequestForm(xboxRequest, methodName, version, sessionId);
            return post(clazz, requestParams);
        }


        /**
         * 发起POST请求(公用方法)
         * @param clazz 类对象
         * @param requestParams 参数集合
         * @param <T>   返回类型
         * @return
         */
        private <T> CompositeResponse post(Class<T> clazz, Map<String, String> requestParams) {
            String responseContent = restTemplate.postForObject(serverUrl, toMultiValueMap(requestParams), String.class);
            logger.info("DefaultClientRequest->>post->>responseContent: " + responseContent);
            return toCompositeResponse(responseContent, clazz);
        }


        /**
         * 构建POST请求
         * @param form 参数集
         * @return
         */
        private MultiValueMap<String, String> toMultiValueMap(Map<String, String> form) {
            MultiValueMap<String, String> mvm = new LinkedMultiValueMap<String, String>();
            for (Map.Entry<String, String> entry : form.entrySet()) {
                mvm.add(entry.getKey(), entry.getValue());
            }
            return mvm;
        }


        /**
         * 发起GET请求
         * @param clazz      类对象
         * @param methodName 方法名
         * @param version    版本号
         * @param <T>        返回类型
         * @return
         */
        @Override
        public <T> CompositeResponse get(Class<T> clazz, String methodName, String version) {
            Map<String, String> requestParams = addOtherSystemParams(methodName, version);
            return get(clazz, requestParams);
        }


        /**
         * 发送GET请求(带会话标识)
         * @param clazz       类对象
         * @param methodName  方法名
         * @param version     版本号
         * @param sessionId   会话标识
         * @param <T>         返回类型
         * @return
         */
        @Override
        public <T> CompositeResponse get(Class<T> clazz, String methodName, String version, String sessionId) {
            Map<String, String> requestParams = addOtherSystemParams(methodName, version, sessionId);
            return get(clazz, requestParams);
        }


        /**
         * 发送GET请求(直接使用)
         * @param xboxRequest 接口对象
         * @param clazz      类对象
         * @param methodName 方法名
         * @param version    版本号
         * @param <T>        返回类型
         * @return
         */
        @Override
        public <T> CompositeResponse get(XboxRequest xboxRequest, Class<T> clazz, String methodName, String version) {
            Map<String, String> requestParams = getRequestForm(xboxRequest, methodName, version);
            return get(clazz, requestParams);
        }


        /**
         * 发送GET请求(直接使用并带会话标识)
         * @param xboxRequest 接口对象
         * @param clazz      类对象
         * @param methodName 方法名
         * @param version    版本号
         * @param sessionId  会话标识
         * @param <T>        返回类型
         * @return
         */
        @Override
        public <T> CompositeResponse get(XboxRequest xboxRequest, Class<T> clazz, String methodName, String version, String sessionId) {
            Map<String, String> requestParams = getRequestForm(xboxRequest, methodName, version, sessionId);
            return get(clazz, requestParams);
        }


        /**
         * 发起Get请求(公用方法)
         * @param clazz 类对象
         * @param requestParams 参数集合
         * @param <T>   返回类型
         * @return
         */
        private <T> CompositeResponse get(Class<T> clazz, Map<String, String> requestParams) {
            String responseContent = restTemplate.getForObject(buildGetUrl(requestParams), String.class, requestParams);
            logger.info("DefaultClientRequest->>get->>responseContent: " + responseContent);
            return toCompositeResponse(responseContent, clazz);
        }


        /**
         * 构建GET请求
         * @param form 参数集
         * @return
         */
        private String buildGetUrl(Map<String, String> form) {
            StringBuilder requestUrl = new StringBuilder();
            requestUrl.append(serverUrl);
            requestUrl.append("?");
            String joinChar = "";
            for (Map.Entry<String, String> entry : form.entrySet()) {
                requestUrl.append(joinChar);
                requestUrl.append(entry.getKey());
                requestUrl.append("=");
                requestUrl.append(entry.getValue());
                joinChar = "&";
            }
            return requestUrl.toString();
        }


        /**
         * 根据响应内容判断，判断是否成功
         * @param content
         * @return
         */
        private boolean isSuccessful(String content) {
            if (MessageFormat.json == messageFormat) {
                return !(content.contains("\"subErrors\":") && content.contains("\"code\":") && content.contains("\"message\":") && content.contains("\"solution\":"));
            } else {
                return !(content.contains("<error") && content.contains("code=\"") && content.contains("<subErrors"));
            }
        }


        /**
         * POST和GET通用方法，返回CompositeResponse对象
         * @param content
         * @param responseClass
         * @param <T>
         * @return
         */
        private <T> CompositeResponse toCompositeResponse(String content, Class<T> responseClass) {
            //判断是否成功
            boolean successful = isSuccessful(content);
            //初始化对象
            DefaultCompositeResponse<T> compositeResponse = new DefaultCompositeResponse<T>(successful);
            //JSON格式
            if (MessageFormat.json == messageFormat) {
                //成功
                if (successful) {
                    //使用fastJsonConverter转换成对象
                    T ropResponse = fastJsonConverter.getObject(content, responseClass);
                    compositeResponse.setSuccessRopResponse(ropResponse);
                } else {
                    //失败设置错误
                    ErrorResponse errorResponse = fastJsonConverter.getObject(content, ErrorResponse.class);
                    compositeResponse.setErrorResponse(errorResponse);
                }
            }
            //XML格式
            else {
                //成功
                if (successful) {
                    //使用xmlConverter转换成对象
                    T ropResponse = xmlConverter.getObject(content, responseClass);
                    compositeResponse.setSuccessRopResponse(ropResponse);
                } else {
                    //失败设置错误
                    ErrorResponse errorResponse = xmlConverter.getObject(content, ErrorResponse.class);
                    compositeResponse.setErrorResponse(errorResponse);
                }
            }
            return compositeResponse;
        }


        /**
         * 添加系统参数
         * @param methodName 方法名
         * @param version    版本号
         * @return
         */
        private Map<String, String> addOtherSystemParams(String methodName, String version) {
            return addOtherSystemParams(methodName, version, sessionId);
        }


        /**
         * 添加系统参数
         * @param methodName 方法名称
         * @param version    参数值
         * @param sessionId  会话标识
         * @return
         */
        private Map<String, String> addOtherSystemParams(String methodName, String version, String sessionId) {
            paramMap.put(SystemParameterNames.getMethod(), methodName);
            paramMap.put(SystemParameterNames.getVersion(), version);
            //判断会话标识，是否添加
            if (null != sessionId) {
                paramMap.put(SystemParameterNames.getSessionId(), sessionId);
            }
            //String signValue = XboxUtils.sign(paramMap, singKey);
            paramMap.put(SystemParameterNames.getExecutionType(), executionType);
            //paramMap.put(SystemParameterNames.getSign(), signValue);
            return paramMap;
        }


        /**
         * 构建请求表单
         * @param xboxRequest 请求对象
         * @param methodName  方法名
         * @param version     版本号
         * @return
         */
        private Map<String, String> getRequestForm(XboxRequest xboxRequest, String methodName, String version) {
            return getRequestForm(xboxRequest, methodName, version, sessionId);
        }


        /**
         * 构建请求表单
         * @param xboxRequest 请求对象
         * @param methodName  方法名
         * @param version     版本号
         * @param sessionId   会话标识
         * @return
         */
        private Map<String, String> getRequestForm(XboxRequest xboxRequest, String methodName, String version, String sessionId) {
            /**
             * 初始化Map
             */
            Map<String, String> form = new LinkedHashMap<String, String>(20);
            /**
             * 添加系统参数
             */
            form.put(SystemParameterNames.getAppKey(), appKey);
            form.put(SystemParameterNames.getMethod(), methodName);
            form.put(SystemParameterNames.getVersion(), version);
            form.put(SystemParameterNames.getFormat(), messageFormat.name());
            form.put(SystemParameterNames.getLocale(), locale.toString());
            form.put(SystemParameterNames.getExecutionType(), executionType);
            form.put(SystemParameterNames.getClient(), "WH_APP");
            form.put(SystemParameterNames.getCallType(), getCallType());
            form.put(SystemParameterNames.getCallVersion(), "1.0.0");
            form.put(SystemParameterNames.getRequestTimestamp(), String.valueOf(getRequestTimestamp()));
            form.put(SystemParameterNames.getSessionId(), StringUtils.isNotEmpty(sessionId) ? sessionId : "");
            //判断数据格式
            String bindObject = "json";
            if (messageFormat == MessageFormat.json) {
                bindObject = fastJsonConverter.getString(xboxRequest);
            }else {
                bindObject = xmlConverter.getString(xboxRequest);
            }
            /**
             * 进行签名
             */
            String signValue = XboxSign.signRule(form, singKey);
            form.put(SystemParameterNames.getSign(), signValue);
            return form;
        }
    }
}

